package com.colorlight.cloud.mqtt.base.core.tls;

import com.colorlight.cloud.common.exception.BaseException;
import lombok.extern.slf4j.Slf4j;

import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;


import javax.net.ssl.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

/**
 * @author Demon
 * ssl配置设置
 * 1. 读客户端证书 链式
 * 2. 读客户端密钥
 * 3. 读CA证书
 */
@Slf4j
public class MySslContext {
	

	/**
	 * 读CA证书
	 *
	 * @param filePath CA证书文件路径
	 * @return 加载后的证书
	 * @author Demon
	 * @date 3/6/2024/下午1:50
	 **/
	private static X509Certificate loadCertificate(String filePath) {
		try (FileInputStream fis = new FileInputStream(filePath)) {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			return (X509Certificate) cf.generateCertificate(fis);
		} catch (Exception e) {
			log.error("", e);
			throw new BaseException("CA证书加载异常");
		}
	}
	
	/**
	 * 读服务端链式证书
	 *
	 * @param pemFilePath 链式证书路径
	 * @return 链式证书
	 * @author Demon
	 * @date 3/6/2024/下午1:49
	 **/
	public static List<X509Certificate> getCertificate(String pemFilePath) {
		
		Security.addProvider(new BouncyCastleProvider());
		List<X509Certificate> certChain;
		try (Reader pemReader = new FileReader(pemFilePath); PEMParser pemParser = new PEMParser(pemReader)) {
			certChain = new ArrayList<>();
			CertificateFactory certFactory = CertificateFactory.getInstance("X.509", "BC");
			
			Object object;
			while ((object = pemParser.readObject()) != null) {
				if (object instanceof X509CertificateHolder) {
					X509CertificateHolder certHolder = (X509CertificateHolder) object;
					X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(
							new java.io.ByteArrayInputStream(certHolder.getEncoded()));
					certChain.add(certificate);
				}
			}
			return certChain;
		} catch (Exception e) {
			log.error("", e);
			throw new BaseException("服务端证书读取异常");
		}
	}
	
	/**
	 * 读取密钥内容
	 * 去除私钥字符串中的标头和尾部信息
	 *
	 * @param clientKeyPath 密钥文件路径
	 * @return 密钥内容
	 * @author Demon
	 * @date 3/6/2024/下午2:26
	 **/
	public static String readClientKey(String clientKeyPath) {
		try {
			return new String(Files.readAllBytes(Paths.get(clientKeyPath)))
					.replace("-----BEGIN RSA PRIVATE KEY-----", "")
					.replace("-----END RSA PRIVATE KEY-----", "")
					.replace("-----BEGIN PRIVATE KEY-----", "")
					.replace("-----END PRIVATE KEY-----", "")
					.replaceAll("\\s", "");
		} catch (IOException e) {
			throw new BaseException("密钥读取异常");
		}
	}
	
	public static PrivateKey loadPrivateKey(String clientKeyPath) {
		
		//解码Base64编码的私钥
		byte[] encoded = Base64.getDecoder().decode(readClientKey(clientKeyPath));
		
		//尝试解析PKCS#8格式
		try {
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			return keyFactory.generatePrivate(keySpec);
		} catch (Exception e) {
			throw new BaseException("PKCS#8格式解析密钥异常");
		}
	}
	
	
	public SSLContext createSSLContext(String clientCertPath, String clientKeyPath, String caCertPath) throws Exception {
		
		
		//读取 服务端证书
		List<X509Certificate> certList = getCertificate(clientCertPath);
		Objects.requireNonNull(certList, "服务端证书读取异常");
		Certificate[] certArray = certList.toArray(Certificate[]::new);
		
		//读取 密钥
		
		PrivateKey clientPrivateKey = loadPrivateKey(clientKeyPath);
		
		//读取 CA证书
		X509Certificate caCertificate = loadCertificate(caCertPath);
		
		KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
		keyStore.load(null, null);
		keyStore.setKeyEntry("client-key", clientPrivateKey, null, certArray);
		
		KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
		trustStore.load(null, null);
		trustStore.setCertificateEntry("ca-cert", caCertificate);
		
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		keyManagerFactory.init(keyStore, null);
		
		TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		trustManagerFactory.init(trustStore);
		
		SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
		sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
		return sslContext;
	}
	
}
